package org.broadway.jmx;

import java.util.HashMap;

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadway.interfaces.ObservedResource;

/**
 * <p>This class represents an observed JMX MBean resource.  It is a proxy object
 * used internally by Broadway to encapsulate and hide the intricacies of
 * manipulating a real JMX directly.  This implementation of ObservedResource
 * provies acces to MBean attributes using a map interface.  So, a call to get({attribute name})
 * will return the attribute value for the specified attribute.</p>
 * 
 * <p>Because this object extends a Map object, it can easily be scripted
 * using the prefered dot (.) or bracket ([]) format in scripting environment
 * when retrieving attributes.  For instance, in Groovy, attribute of an MBean using this
 * class can be retrieved using a syntax similar to "mbean.attributeName" or 
 * "mbean[atributeName]".
 *
 * @author vladimir vivien
 * 
 * @see ObservedResource
 *
 */
public class ObservedJmxBean extends HashMap<String, Object> implements ObservedResource {

    private static final Log log = LogFactory.getLog(ObservedJmxBean.class);
    private static final long serialVersionUID = 3107866832588631790L;
    private MBeanServerConnection svrConnection;
    private ObjectName objectName;

    public ObservedJmxBean() {
    }

    /**
     * Constructor for ObservedJmxBean.
     * @param svrConn - an MBean server where the true MBean is registered.
     * @param objName - the ObjectName for the MBean.
     * @throws JmxException - thrown if anything goes wrong.
     */
    public ObservedJmxBean(MBeanServerConnection svrConn, ObjectName objName) throws JmxException {
        this.setMBeanServer(svrConn);
        this.setObjectName(objName);
    }

    /**
     * Constructor for ObservedJmxBean.
     * @param svrConn - server where MBean is registered.
     * @param objName - a string representation of the MBean's object name.
     * @throws JmxException - thrown if anything goes wrong during creation.
     */
    public ObservedJmxBean(MBeanServerConnection svrConn, String objName) throws JmxException {
        setMBeanServer(svrConn);
        setObjectName(JmxUtil.createObjectName(objName));
    }

    /**
     * Setter for the Observed resource's id.  In this case, the id is the MBean's JMX object name.
     * @param id - the id value.
     */
    public void setId(Object id) {
        if (id != null) {
            setObjectName(JmxUtil.createObjectName((String) id));
        }
    }

    /**
     * Getter for id.
     * @return String - id.
     */
    public Object getId() {
        if (objectName != null) {
            return objectName.getCanonicalName();
        }
        return null;
    }

    /**
     * A setter for the MBeanServer where MBean is registered.
     * @param server - instance of MBean server.
     */
    public void setMBeanServer(MBeanServerConnection server) {
        svrConnection = server;
    }

    /**
     * Getter for MBean server.
     * @return MBeanServer
     */
    public MBeanServerConnection getMBeanServer() {
        return svrConnection;
    }

    /**
     * A setter for ObjectName.  You can use this or the setId().  They both represnt the same thing.
     * @param name
     */
    public void setObjectName(ObjectName name) {
        this.objectName = name;
    }

    /**
     * Returns an instance of ObjectName for the JMX object being observed.
     * @return ObjectName
     */
    public ObjectName getObjectName() {
        return objectName;
    }

    /**
     * Returns the attribute value for the specified JMX MBean attribute.
     * @param attribute - an MBean attribute associated with observed MBean.
     * @return Object the value for that attribute.
     */
    @Override
    public Object get(Object attribute) {
        if (attribute == null) {
            return null;
        }

        Object result = null;
        String upperAttr = org.apache.commons.lang.WordUtils.capitalize((String) attribute);
        String lowerAttr = org.apache.commons.lang.WordUtils.uncapitalize((String) attribute);

        try {
            
            result = JmxUtil.getAttributeValue(svrConnection, getObjectName(), upperAttr);
            
        } catch (JmxException e) {
            // try again with standard JavaBean name (lower case first letter)
            result = JmxUtil.getAttributeValue(svrConnection, getObjectName(), lowerAttr);
        }
        
        log.debug("Attribute [" + attribute + "] with value [" + result + "] retreived for object [" + getObjectName() + "]");
        return result;
    }

    /**
     * This method is not implemented.  It prevents an observed MBean attribute value to 
     * be updated.
     * @param key
     * @param val
     * @return Object
     */
    @Override
    public Object put(String key, Object val) {
        throw new UnsupportedOperationException("You are not allowed to change JMX Bean attribute values during monitoring.");
    }
}
